package birdsong.server;

import java.util.LinkedList;

import birdsong.comm.interfaces.ServerComm;
import birdsong.comm.interfaces.ServerSideMessage;


public class Server implements BirdsongServer{

	protected ServerComm serverComm;
	
	private LinkedList<ServerClient> clients;

	/**
	 * Initiate the Server
	 */
	@Override
	public void start(ServerComm serverComm) {

		this.serverComm=serverComm;
		
		this.clients=new LinkedList<ServerClient>(); 
		
		

		serverComm.start();

		System.out.println("SERVER  :  Start");
		
		while (true) {


			//if(serverComm.hasNextMessage()){
				if(1==1){
				System.out.println("SERVER  :  Message Received");
				
				ServerSideMessage message = serverComm.getNextMessage();
				System.out.println("SERVER MESSAGE->"+message);

				switch (message.getType()) {
				case CLIENT_CONNECTED:
					System.out.println("SERVER  :  Message Received  :  CLIENT_CONNECTED");
					//CONNECTS A CLIENT TO THE SERVER
					clientConnected(message.getClientNickname());
					break;
				case CLIENT_DISCONNECTED:
					//DISCONNECTES A CLIENT FROM THE SERVER (REMOVES THE CLIENT FROM THE ONLINE CLIENTS)
					System.out.println("SERVER  :  Message Received  :  CLIENT_DISCONNECTED");
					clientDisconnected(message.getClientNickname());
					break;
				case BIRDSONG_MESSAGE:
					//FORWARDS A MESSAGE TO THE CLIENTS FOLLOWER
					System.out.println("SERVER  :  Message Received  :  BIRDSONG_MESSAGE");
					message(message.getClientNickname(), message.getPayload());
					break;
				case FOLLOW:
					//ADDS A FOLLOWER TO A CERTAIN CLIENT
					System.out.println("SERVER  :  Message Received  :  FOLLOW");
					follow(message.getClientNickname(), message.getPayload());
					break;
				case UNFOLLOW:
					//A CERTAIN CLIENT IS REMOVED FROM A CLIENT FOLLOWERS LIST
					System.out.println("SERVER  :  Message Received  :  UNFOLLOW");
					unfollow(message.getClientNickname(), message.getPayload());
					break;

				default:
					break;
				}
			}
		}
	}


	////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////
	//THIS METHODS ARE PUBLIC JUST FOR TESTING
	////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////


	public LinkedList<ServerClient> getClients() {
		return clients;
	}


	/**
	 * Creates a new server client that is added to the server online list
	 * @param nickName	 Nickname of the new client
	 */
	public void clientConnected(String nickName) {

		boolean userAlreadyExists = verifyClient(nickName);

		if(!userAlreadyExists){

			ServerClient newClient = new ServerClient(nickName);

			sendCurrentOnlineUsersToNewClient(newClient);

			clients.add(newClient);
			System.out.println("server print: "+this);

		}else{

			serverComm.sendError(nickName, "THE CHOOSEN NICKNAME ALREADY EXISTS");

		}
	}

	/**
	 * Send a message to all online user when a client connects to the server
	 * @param newClient
	 */
	private void sendCurrentOnlineUsersToNewClient(ServerClient newClient) {
		if(!clients.isEmpty())
			for (ServerClient onlineClients : clients) {

				//SEND TO CONNECTED USER THE NEW CLIENT
				serverComm.sendClientConnected(onlineClients.getNickName(), newClient.getNickName());

				//SEND TO NEW CLIENT THE CONNECTED USERS
				serverComm.sendClientConnected(newClient.getNickName(), onlineClients.getNickName());
			}

	}

	/**
	 * Verify if the list is empty and if there is a user with the same nickname
	 * @param nickName
	 * @return false if there is not an user with the same nickname or if the list is empty, and returns true if there is a user with the same name
	 */
	public boolean verifyClient(String string) {

		if(!clients.isEmpty())	
			for (ServerClient client : clients) {
				if(client.getNickName().equals(string)){
					return true;
				}
			}
		return false;
	}

	/**
	 * Disconnects a server client from the online users list with the given nickname 
	 * @param clientNickname
	 */
	public void clientDisconnected(String clientNickname) {

		ServerClient disconnetedClient = getClient(clientNickname, clients);

		if(disconnetedClient!=null){

			clients.remove(disconnetedClient);

			//THIS IS COMMENTED BECAUSE WE DO NOT HAVE THE SERVERCOM
			sendDisconnectedClient(clientNickname);

			for (ServerClient client : clients) {

				client.removeFollower(getClient(clientNickname, client.getFollowers()));

			}
		}else{
			//THIS IS COMMENTED BECAUSE WE DO NOT HAVE THE SERVERCOM
						serverComm.sendError(clientNickname, "The Client Does Not Exists");
		}

	}

	/**
	 * Send to current online users the information that the clientNickname has disconnected
	 * @param clientNickname 
	 */
	private void sendDisconnectedClient(String clientNickname) {
		if(!clients.isEmpty()){
			for (ServerClient onlineClients : clients) {
				serverComm.sendClientDisconnected(onlineClients.getNickName(), clientNickname);
			}
		}else{
			//serverComm.sendError(clientNickname, "There is not any client");
		}
	}


	/**
	 * Get the client with certain nickname from a list of clients, results in null if the given list is empty or if the client nickname does not exist;
	 * If there is a client in the given list, that client is returned
	 * @param clientNickname Nickname from the client that is searched
	 * @param clients List of clients
	 * @return null if the list is empty or if the client not exist, returns the client if the client exists in the given list.
	 */
	public ServerClient getClient(String clientNickname, LinkedList<ServerClient> clients) {

		if(!clients.isEmpty())	
			for (ServerClient client : clients) {
				if(client.getNickName().equals(clientNickname)){

					return client;
				}
			}
		return null;
	}


	/**
	 * Forwards a message to the 
	 * @param clientNickname
	 * @param payload
	 */
	public void message(String clientNickname, String payload) {

		ServerClient client = getClient(clientNickname, clients);

		if(client!=null){
			for (ServerClient follower : client.getFollowers()) {
				//THIS IS COMMENTED BECAUSE WE DO NOT HAVE THE SERVERCOM
				serverComm.sendBirdsongMessage(client.getNickName(), follower.getNickName(), payload);
			}
		}else{
			//THIS IS COMMENTED BECAUSE WE DO NOT HAVE THE SERVERCOM
			serverComm.sendError(clientNickname, "client dont exists");
		}
	}

	/**
	 * ClientNickname will follow the clientToFollowNickname, client is added to the list of followers of the client to follow
	 * @param clientNickname
	 * @param clientToFollowNickname
	 */
	public void follow(String clientNickname, String clientToFollowNickname) {

		ServerClient client = getClient(clientNickname, clients);

		ServerClient clientToFollow = getClient(clientToFollowNickname, clients);

		if(client!=null && clientToFollow!=null){

			clientToFollow.addFollower(client);
			//THIS IS COMMENTED BECAUSE WE DO NOT HAVE THE SERVERCOM
			serverComm.sendNewFollower(clientToFollowNickname, clientNickname);


		}else{
			//THIS IS COMMENTED BECAUSE WE DO NOT HAVE THE SERVERCOM
			serverComm.sendError(clientToFollowNickname, "client does not exists");
		}
	}


	/**
	 * Removes a client from the followers list of another client
	 * @param clientNickname The nickname from t
	 * @param clientToUnfollowNickname 
	 */
	public void unfollow(String clientNickname, String clientToUnfollowNickname) {

		ServerClient client = getClient(clientNickname, clients);

		ServerClient clientToUnfollow = getClient(clientToUnfollowNickname, clients);

		if(!client.equals(null) && !clientToUnfollow.equals(null)){	

			clientToUnfollow.removeFollower(client);

			//THIS IS COMMENTED BECAUSE WE DO NOT HAVE THE SERVERCOM
					serverComm.sendNewUnfollow(clientToUnfollow.getNickName(), client.getNickName());

		}
	}


	@Override
	public String toString() {
		return "Server [clients=" + clients.toString() + "]";
	}


}
